package org.ihelpuoo.weboo.module;

import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;
import org.ihelpuoo.http.HTTP;
import org.ihelpuoo.json.JSON;
import org.ihelpuoo.lang.ExceptionUtils;
import org.ihelpuoo.lang.LocaleDescribable;
import org.ihelpuoo.lang.ObjectUtils;
import org.ihelpuoo.lang.StringUtils;
import org.ihelpuoo.parser.segment.Segment;
import org.ihelpuoo.weboo.DataChecker;
import org.ihelpuoo.weboo.Weboo;
import org.ihelpuoo.weboo.WebooUtils;
import org.ihelpuoo.weboo.action.http.Action;
import org.ihelpuoo.weboo.bean.ActionBean;
import org.ihelpuoo.weboo.bean.LocaleModuleBean;
import org.ihelpuoo.weboo.bean.ModuleBean;
import org.ihelpuoo.weboo.bean.ParamBean;
import org.ihelpuoo.weboo.checkers.CheckError;
import org.ihelpuoo.weboo.exception.ActionConfigInvalidException;
import org.ihelpuoo.weboo.exception.ActionDefineNotFoundException;
import org.ihelpuoo.weboo.exception.ActionInstanceException;
import org.ihelpuoo.weboo.exception.ActionNotFoundException;
import org.ihelpuoo.weboo.security.SessionImpl;
import org.ihelpuoo.weboo.util.ForwardTarget;
import org.ihelpuoo.weboo.util.HttpLog;
import org.ihelpuoo.xml.XMLIO;
import org.w3c.dom.Document;

public final class Module implements LocaleDescribable
{
	private static Logger		log	= Logger.getLogger(Module.class);

	private String				name;
	private ModuleContext		moduleContext;
	private Charset				inputCharset;
	private Charset				outputCharset;
	private ActionManager		actionManager;
	private String				comment;
	private ModuleInstaller		installer;
	private ModuleUninstaller	uninstaller;
	private ModuleBean			bean;

	ActionManager getActionManager()
	{
		return actionManager;
	}

	public String getActionHome()
	{
		return actionManager.getHome();
	}

	void setActionManager(ActionManager actionManager)
	{
		this.actionManager = actionManager;
	}

	public ModuleContext getModuleContext()
	{
		return moduleContext;
	}

	void setModuleContext(ModuleContext moduleContext)
	{
		this.moduleContext = moduleContext;
	}

	public String getName()
	{
		return name;
	}

	void setName(String name)
	{
		this.name = name;
	}

	public String getComment()
	{
		return comment;
	}

	void setComment(String comment)
	{
		this.comment = comment;
	}

	public ModuleUninstaller getUninstaller()
	{
		return this.uninstaller;
	}

	void setUninstaller(ModuleUninstaller uninstaller)
	{
		this.uninstaller = uninstaller;
	}

	public ModuleInstaller getInstaller()
	{
		return installer;
	}

	void setInstaller(ModuleInstaller installer)
	{
		this.installer = installer;
	}

	void setInputCharset(Charset inputCharset)
	{
		this.inputCharset = inputCharset;
	}

	void setOutputCharset(Charset outputCharset)
	{
		this.outputCharset = outputCharset;
	}

	public Charset getInputCharset()
	{
		return inputCharset;
	}

	public Charset getOutputCharset()
	{
		return outputCharset;
	}

	protected void processAction(String actionName, List<IController> controllers,
			HttpServletRequest request, HttpServletResponse response)
	{
		try
		{
			if (null == actionManager)
				throw new ActionDefineNotFoundException(this);
			request.setCharacterEncoding(inputCharset.name());
			if (HttpLog.log.isInfoEnabled())
				HttpLog.dumpBrief(request);
			ActionBean actionBean = actionManager.getActionBean(actionName);
			if (null == actionBean)
				throw new ActionDefineNotFoundException(this,actionName);

			ActionConfig config = ActionConfig.wrap(actionBean,moduleContext.getModuleManager());
			ActionContext context = new ActionContext(moduleContext,config,request,response);
			Module m = this;
			String newName = this.doDispatch(context,controllers);
			Action action = null;
			if (null != newName)
			{
				ForwardTarget an = new ForwardTarget(newName);
				m = moduleContext.getModuleManager().getModule(an.getModuleName());
				config = m.getActionManager().getActionConfig(an.getActionName());
				context = new ActionContext(m.getModuleContext(),config,request,response);
			}
			action = config.getActionBean().getAction();
			response.setCharacterEncoding(outputCharset.name());
			List<CheckError> errors = checkParams(config,context);
			if (null == errors || errors.size() == 0)
				action.execute(context);
			else
			{
				response.setStatus(400);
				if (context.getOutputType().isJson())
					response.getWriter().write(JSON.toJSONString(errors,false));
				else
				{
					Document doc = context.pojo().createDocument(errors);
					XMLIO.printDocument(doc,response.getOutputStream(),true);
				}
				response.flushBuffer();
			}
		}
		catch (ActionDefineNotFoundException e)
		{
			log.warn(ExceptionUtils.dumpBrief(e));
			response.setStatus(404);
			dumpException(response,e);
		}
		catch (ActionNotFoundException e)
		{
			log.warn(ExceptionUtils.dumpBrief(e));
			response.setStatus(404);
			dumpException(response,e);
		}
		catch (ActionInstanceException e)
		{
			log.warn(ExceptionUtils.dumpBrief(e));
			response.setStatus(404);
			dumpException(response,e);
		}
		catch (Throwable e)
		{
			treateException(actionName,request,e);
			response.setStatus(500);
			dumpException(response,e);
		}
	}

	private void dumpException(HttpServletResponse response, Throwable e)
	{
		try
		{
			e.printStackTrace(response.getWriter());
		}
		catch (IOException e1)
		{
			log.fatal(ExceptionUtils.dump(e));
		}
	}

	private void treateException(String actionName, HttpServletRequest request, Throwable e)
	{
		log.error("!" + this.name + "] > " + actionName);
		log.error(ExceptionUtils.dumpBrief(e));
		if (log.isDebugEnabled())
			log.debug(HTTP.dumpHeaders(request));
		if (log.isTraceEnabled())
			log.trace(ExceptionUtils.dump(e));
	}

	protected void processHTML(Segment seg, List<IController> controllers,
			HttpServletRequest request, HttpServletResponse response)
	{
		try
		{
			if (HttpLog.log.isInfoEnabled())
				HttpLog.dumpBrief(request);
			request.setCharacterEncoding(inputCharset.name());
			WebooUtils.formatSegment(getModuleContext(),SessionImpl.wrap(request).getLocale(),seg);
			response.setCharacterEncoding(outputCharset.name());
			response.getWriter().write(seg.toString());
			response.flushBuffer();
		}
		catch (Exception e)
		{
			treateException("HTML \"" + name + "\"",request,e);
		}
	}

	private List<CheckError> checkParams(ActionConfig config, ActionContext context)
	{
		List<CheckError> list = new LinkedList<CheckError>();
		Iterator<ParamBean> it = config.paramIterator();
		if (null == it)
			return null;

		while (it.hasNext())
		{
			ParamBean param = it.next();
			String v = context.params().getString(param.getName());
			if ((StringUtils.isEmpty(v) && param.isRequired()))
				list.add(new CheckError(DataChecker.EMPTY_VALUE).formatByContext(context,param));
			else if (!param.isValid(v))
				list.add(new CheckError(DataChecker.INVALID_VALUE).formatByContext(context,param));
		}
		return list;
	}

	private String doDispatch(ActionContext context, List<IController> controllers)
			throws Exception
	{
		if (null != controllers)
		{
			Iterator it = controllers.iterator();
			while (it.hasNext())
			{
				String a = ((IController)it.next()).dispatch(context);
				if (null != a)
					return a;
			}
		}
		return null;
	}

	public String getDescription()
	{
		StringBuffer sb = new StringBuffer();
		sb.append("\r\n    > " + name);
		if (null != comment)
			sb.append("\r\n        " + StringUtils.trim(comment));
		if (moduleContext.actions() != null)
		{
			Iterator<String> it = moduleContext.actions().iterator_action_name();
			while (it.hasNext())
			{
				String name = it.next();
				ActionConfig ac;
				try
				{
					ac = moduleContext.actions().getActionConfig(name);
					sb.append("\r\n       * ").append(ac.getFullName());
				}
				catch (ActionConfigInvalidException e)
				{
					sb.append(e.getMessage());
				}
			}
		}
		List<Locale> locales = moduleContext.getLocales();
		if (null == locales)
			sb.append("\r\n    !!!NOT LOCALEs");
		else
		{
			sb.append("\r\n       @ LOCALEs:");
			Iterator<Locale> it = locales.iterator();
			while (it.hasNext())
			{
				Locale l = it.next();
				if (null == l)
					sb.append("\r\n           - " + Weboo.DEFAULT_LOCALE_NAME);
				else
					sb.append("\r\n           - " + l.toString());
			}
		}
		return sb.toString();
	}

	public String getAlias(Locale locale)
	{
		return this.getModuleContext().getMessage(locale,name);
	}

	public String getDescription(Locale locale)
	{
		return this.getDescription();
	}

	void setBean(ModuleBean bean)
	{
		this.bean = bean;
	}

	public ModuleBean getBean()
	{
		return bean;
	}

	public LocaleModuleBean getLocaleBean(Locale locale)
	{
		LocaleModuleBean md = (LocaleModuleBean)ObjectUtils
				.duplicateAs(bean,LocaleModuleBean.class);
		md.setAlias(getAlias(locale));
		return md;
	}
}
